﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace RNNLibrary
{
    /// <summary>
    /// 原始版RNN算法库
    /// </summary>
    public class OriginalRNN
    {
        #region 属性
        /// <summary>
        /// 神经元列表
        /// </summary>
        private List<Neurons> NList { get; set; }
        /// <summary>
        /// 上一时序隐藏层的权值
        /// </summary>
        private decimal W { get; set; }
        /// <summary>
        /// 输入量的权值
        /// </summary>
        private decimal U { get; set; }
        /// <summary>
        /// 输出层的权值
        /// </summary>
        private decimal V { get; set; }
        /// <summary>
        /// 损失值
        /// </summary>
        public decimal LossVal { get; private set; }
        private string fileName = "trainRecord.csv";
        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="xlist">输入值列表</param>
        /// <param name="ylist">输出值列表</param>
        public OriginalRNN(List<decimal> xlist,List<decimal> ylist)
        {
            Random r = new Random(DateTime.Now.Second);
            U = (decimal)r.Next(1, 100);
            V = (decimal)r.Next(1, 100);
            W = (decimal)r.Next(1, 100);
            NList = new List<Neurons>();
            for(int i=0;i<xlist.Count;i++)
            {
                Neurons n = new Neurons(xlist[i],ylist[i]);
                if(i!=0)
                {
                    n = new Neurons(xlist[i], ylist[i], NList[i - 1]);
                }
                NList.Add(n);
            }
        }

        /// <summary>
        /// 训练神经网络
        /// </summary>
        public void Train()
        {
            decimal deltaH = 0;
            decimal deltaW = 0;
            decimal deltaU = 0;
            decimal deltaV = 0;
            LossVal = 0;
            decimal α = 0.1M;
            foreach(Neurons n in NList)
            {
                n.Forward(W, U, V);
            }
            StringBuilder record = new StringBuilder("神经元编号,h导数,w导数,u导数,v导数,误差值\r\n");
            for (int i = NList.Count - 1; i >= 0; i--)
            {
                Neurons n = NList[i];
                n.Reverse(deltaH, W, V);
                deltaH = n.DeltaH;
                n.SumDeltaHyperparameter(ref deltaW, ref deltaU, ref deltaV);
                LossVal += Math.Abs(n.LossVal);
                record.Append(n.CurrentT+","+deltaH+","+deltaW+","+deltaU+","+deltaV + "," + LossVal+"\r\n");
            }
            record.Append("学习前：w" + W + "。u" + U + "。v" + V+"\r\n");
            V += α * deltaV;
            U += α * deltaU;
            W += α * deltaW;
            record.Append("学习后：w" + W + "。u" + U + "。v" + V);
            File.WriteAllText(fileName, record.ToString(),Encoding.UTF8); 
        }

        /// <summary>
        /// 预测Y值
        /// </summary>
        /// <param name="xlist"></param>
        /// <returns></returns>
        public List<decimal> Prediction(List<decimal> xlist)
        {
            List<decimal> ylist = new List<decimal>();
            for(int i = 0; i < NList.Count; i++)
            {
                Neurons n = NList[i];
                n.X = xlist[i];
                n.Forward(W, U, V);
                ylist.Add(n.Aims);
            }
            return ylist;
        }
    }
}
